package top.lshaci.framework.utils;

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.YearMonth;
import java.time.temporal.WeekFields;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Stream;

import static java.time.DayOfWeek.MONDAY;
import static java.time.temporal.ChronoUnit.*;
import static java.util.stream.Collectors.toList;

/**
 * ContinuousDateData
 *
 * @author lshaci
 * @since 1.0.8
 */
public class ContinuousDateData {

    /**
     * <p>生成每一天的数据</p>
     * <pre>
     *     [start, end)
     *
     *     示例:
     *     Map&lt;String, Object&gt; data = new HashMap&lt;&gt;();  // key: dayStr (示例: 2020-10-10)
     *     List&lt;LabelValueVO&gt; day = day(startDate, endDate, dayStr -&gt; {
     *         Object o = data.get(dayStr);
     *         return LabelValueVO.of(dayStr, Optional.ofNullable(o).orElse(0));
     *     });
     * </pre>
     *
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @param mapper    生成数据函数
     * @param <R>       成数据格式
     * @return 每天的数据集合
     */
    public static <R> List<R> day(LocalDate startDate, LocalDate endDate, final Function<String, ? extends R> mapper) {
        return Stream.iterate(startDate, s -> s.plusDays(1))
                .limit(startDate.until(endDate, DAYS))
                .map(LocalDate::toString)
                .map(mapper)
                .collect(toList());
    }

    /**
     * 生成每一周的数据
     * <pre>
     *     [start, end)
     *
     *     示例:
     *     Map&lt;String, Object&gt; data = new HashMap&lt;&gt;(); // key: year + "-" + week (示例: 2020-40)
     *     List&lt;LabelValueVO&gt; week = week(startDate, endDate, day -&gt; {
     *         int week = day.get(WeekFields.ISO.weekOfWeekBasedYear());
     *         String key = day.getYear() + "-" + week;
     *         Object o = data.get(key);
     *         // label=2020-09-28, value=0 日期为<b>星期一</b>
     *         // return LabelValueVO.of(key, Optional.ofNullable(o).orElse(0));
     *         // label=2020-40, value=0
     *         return LabelValueVO.of(day.toString(), Optional.ofNullable(o).orElse(0));
     *     });
     * </pre>
     *
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @param mapper    生成数据函数
     * @param <R>       成数据格式
     * @return 每天的数据集合
     */
    public static <R> List<R> week(LocalDate startDate, LocalDate endDate, final Function<LocalDate, ? extends R> mapper) {
        return week(startDate, endDate, mapper, MONDAY);
    }

    /**
     * 生成每一周的数据
     * <pre>
     *     [start, end)
     *
     *     示例:
     *     Map&lt;String, Object&gt; data = new HashMap&lt;&gt;(); // key: year + "-" + week (示例: 2020-40)
     *     List&lt;LabelValueVO&gt; week = week(startDate, endDate, day -&gt; {
     *         int week = day.get(WeekFields.ISO.weekOfWeekBasedYear());
     *         String key = day.getYear() + "-" + week;
     *         Object o = data.get(key);
     *         // label=2020-09-28, value=0 指定的dayOfWeek
     *         // return LabelValueVO.of(key, Optional.ofNullable(o).orElse(0));
     *         // label=2020-40, value=0
     *         return LabelValueVO.of(day.toString(), Optional.ofNullable(o).orElse(0));
     *     }, DayOfWeek.MONDAY);
     * </pre>
     *
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @param mapper    生成数据函数
     * @param dayOfWeek 用于label为yyyy-MM-dd格式
     * @param <R>       成数据格式
     * @return 每星期的数据集合
     */
    public static <R> List<R> week(LocalDate startDate, LocalDate endDate, final Function<LocalDate, ? extends R> mapper, DayOfWeek dayOfWeek) {
        LocalDate startWeek = startDate.with(WeekFields.ISO.dayOfWeek(), dayOfWeek.getValue());
        LocalDate endWeek = endDate.with(WeekFields.ISO.dayOfWeek(), dayOfWeek.getValue());

        return Stream.iterate(startWeek, s -> s.plusWeeks(1))
                .limit(startWeek.until(endWeek, WEEKS))
                .map(mapper)
                .collect(toList());
    }

    /**
     * <p>生成每一月的数据</p>
     * <pre>
     *     [start, end)
     *
     *     示例:
     *     Map&lt;String, Object&gt; data = new HashMap&lt;&gt;(); // key: monthStr (示例: 2020-10)
     *     List&lt;LabelValueVO&gt; month = month(startDate, endDate, monthStr -&gt; {
     *         Object o = data.get(monthStr);
     *         return LabelValueVO.of(monthStr, Optional.ofNullable(o).orElse(0));
     *     });
     * </pre>
     *
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @param mapper    生成数据函数
     * @param <R>       成数据格式
     * @return 每月的数据集合
     */
    public static <R> List<R> month(LocalDate startDate, LocalDate endDate, final Function<String, ? extends R> mapper) {
        final YearMonth startMonth = YearMonth.of(startDate.getYear(), startDate.getMonth());
        return Stream.iterate(0, i -> i + 1)
                .limit(startDate.until(endDate, MONTHS))
                .map(startMonth::plusMonths)
                .map(YearMonth::toString)
                .map(mapper).collect(toList());
    }
}
